﻿using System.Collections.Generic;
using ZquenceStudio3Host.DspPlugins.Synthesizers.Nodesizer.Engine.Modules.Container;
using ZquenceStudio3Host.Commands.BuiltInCommands;
using ZquenceStudio3Host.DspPlugins.Synthesizers.Nodesizer.Engine.GUI;
using DefaultDspPlugins.UI.Common.Modulizer.Engine.GUIv2.FrontEndComponents;
using SharpDepend.Datatypes;

namespace ZquenceStudio3Host.DspPlugins.Synthesizers.Nodesizer.Engine.Commands
{
    public class AddModuleCommand : BaseCommand
    {
        private IModuleContainer mParentModule;
        private BaseModule mModule;
        private BaseModulizerUI mNodisizer;
        private Vector4 mBound;
        private Dictionary<ModuleInput, ModuleOutput> mCurrentInputConnections = new Dictionary<ModuleInput, ModuleOutput>();
        private Dictionary<ModuleOutput, List<ModuleInput>> mCurrentOutputConnections = new Dictionary<ModuleOutput, List<ModuleInput>>();
        private bool mHasBeenRemoved;
        private ModulizerUIv2 mNodisizer1;

        public override object[] ReferenceObjects
        {
            get { return RefereceObject(mParentModule); }
        }

        public BaseModule AddedModule { get { return mModule; } }

        public AddModuleCommand(BaseModulizerUI nodisizer, Vector4 bound, BaseModule module)
        {
            this.mNodisizer = nodisizer;
            this.mModule = module;
            this.mParentModule = module.ParentModule as IModuleContainer;
            this.mBound = bound;
        }

        public AddModuleCommand(ModulizerUIv2 mNodisizer1, Vector4 bound, BaseModule module)
        {
            this.mNodisizer1 = mNodisizer1;
            this.mBound = bound;
            this.mParentModule = module.ParentModule as IModuleContainer;
            this.mModule = module;
        }
        
        protected override void Execute()
        {
            mModule.GuiPosition = mBound;

            if (mHasBeenRemoved)
            {
                mParentModule.AddSubModule(mModule);
            }

            // Connect all inputs.
            foreach (var input in mCurrentInputConnections)
            {
                ModuleInput moduleInput = input.Key;
                ModuleOutput moduleOutput = input.Value;
                moduleInput.FromOutput = moduleOutput;
            }

            // Connect all outputs.
            foreach (var output in mCurrentOutputConnections)
            {
                ModuleOutput moduleOutput = output.Key;
                List<ModuleInput> moduleInputs = output.Value;

                for (int i = moduleInputs.Count - 1; i >= 0; i--)
                {
                    moduleOutput.AddInput(moduleInputs[i]);
                }
            }

            // Connect all parameters.
            // ...

            mCurrentInputConnections.Clear();
            mCurrentOutputConnections.Clear();

            if (mNodisizer1 != null)
            {
                //// If parent is set, then this command runs several and will call Rebuild to often. Parent is responsibillity to call it instead.
                if (ParentCommand == null)
                {
                    bool animateToFill = mModule.ParentModule != mNodisizer1.CurrentModuleContainer;
                    mNodisizer1.GoInto(mModule.ParentModule, animateToFill);
                    mNodisizer1.UpdateModuleUIUp(mModule.ParentModule);
                }

                mNodisizer1.UpdateParameterStates();
            }
            else
            {
                // If parent is set, then this command runs several and will call Rebuild to often. Parent is responsibillity to call it instead.
                if (ParentCommand == null)
                {
                    mNodisizer.RebuildSynthGraphModuleParent(mModule);
                }

                mNodisizer.UpdateParameterStates();
            }
        }

        protected override void UnExecute()
        {
            mHasBeenRemoved = true;

            mCurrentInputConnections.Clear();
            mCurrentOutputConnections.Clear();

            // Disconnect all inputs.
            foreach (var input in mModule.Inputs)
            {
                if (input.IsConnected)
                {
                    mCurrentInputConnections.Add(input, input.FromOutput);
                    input.FromOutput = null;
                }
            }

            // Disconnect all outputs.
            foreach (var output in mModule.Outputs)
            {
                if (output.IsConnected)
                {
                    mCurrentOutputConnections.Add(output, new List<ModuleInput>());
                    for (int i = output.ConnectedInputs.Count - 1; i >= 0; i--)
                    {
                        ModuleInput input = output.ConnectedInputs[i];
                        mCurrentOutputConnections[output].Add(input);
                        output.RemoveInput(input);
                    }
                }
            }

            // Disconnect all parameters.
            // ...

            GroupModule parent = mModule.ParentModule as GroupModule;

            // Remove module from engine.
            mParentModule.RemoveSubModule(mModule);
            
            if (mNodisizer1 != null)
            {
                // If parent is set, then this command runs several and will call Rebuild to often. Parent is responsibillity to call it instead.
                if (ParentCommand == null)
                {
                    bool animateToFill = mModule.ParentModule != mNodisizer1.CurrentModuleContainer;
                    mNodisizer1.GoInto(parent, animateToFill);
                    mNodisizer1.UpdateModuleUIUp(parent);
                }
                
                mNodisizer1.UpdateParameterStates();
            }
            else
            {
                // If parent is set, then this command runs several and will call Rebuild to often. Parent is responsibillity to call it instead.
                if (ParentCommand == null)
                {
                    mNodisizer.RebuildSynthGraph(parent);
                }

                mNodisizer.UpdateParameterStates();
            }
        }
        
        public override string ToString()
        {
            return "Added module.";
        }
    }
}
